/* Copyright 2014 Jan Wolter */

/* This has a better heuristic function ... which doesn't actually seem to be
 * much better. Oh well.
 */
#include "stack.h"
#include "tableau.h"
#include "found.h"
#include "course.h"
#include "stock.h"
#include "solve.h"
#include "game.h"

char *gamename= "All In A Row";
char *gameopt= "P";
Stack *gathered= NULL;
Stack *temp= NULL;

int nplayed= 0;
int nwon= 0;
int nabandoned= 0;
int cardsleft= 0;
int maxoff;
int prune= 0;

void setopt(char c)
{
    switch (c)
    {
	case 'P': prune= 1; break;
    }
}


/* This is the main program for setting up the current game */
void exec(int gather, int arif, int over, int cut)
{
    /* The Rankdata array will count how many cards of each type are still
     * in the game, that is, have not yet been moved to the foundation.
     */
    rankdata= (int *)malloc(14*sizeof(int));
    for (int i= 1; i <= 13; i++)
	rankdata[i]= 4;

    /* Build and shuffle the deck */
    Stack *shuf= mixcards(arif, over, cut);

    /* Create the waste/foundation thingy */
    makeCourse(1,1,NULL);

    /* Create the tableau */
    for (int i= 1; i <= 13; i++)
	makeTableau(4, FLIP_NONE, shuf,
	    S_NONE, 0, S_NONE, 0, 1, F_NONE, BREAK_NOWHERE, 0);

    freeStack(shuf);

    if (verbose > 1) printState(stdout, stacks);

    maxoff= 0;
    int win= solve(gather,0);

    if (verbose > 0) printf("maxoff=%d\n",maxoff);
    nplayed++;
    cardsleft+= maxoff;
    if (win == 1)
	nwon++;
    else if (win == 2)
	nabandoned++;

    cleanFound();
    cleanTableau();
    cleanCourse();
    cleanStacks();
    cleanVisited();
}

void gatherAll()
{
    if (gathered == NULL)
	gathered= allocStack(ncard);
    else
	gathered->n= 0;

    gatherTableau(gathered);
    gatherCourse(gathered,0);
    if (gathered->n != ncard)
    {
	printf("Gather Failed - found %d cards\n",gathered->n);
	exit(1);
    }
}

/* For the game to be winnable, it must always be true that
 *  rankdata[i] <= rankdata[i-1] + rankdata[i+1] for all ranks i.
 * Before we decrease the count for a rank we should check that it
 * won't break the inequality for either neighbor.
 *
 * DOESNT REALLY WORK
 */

int islands(int n)
{
    /* One might be tempted to subtract one from nx because we are about
     * to remove one card of this rank, but that card will then be on top
     * of the foundation, and can thus still be used to remove adjacent cards.
     */
    int nx= rankdata[n];

    int nxm1= rankdata[(n+11)%13 + 1];
    int nxm2= rankdata[(n+10)%13 + 1];
    if (nxm1 > nxm2 + nx) return 1;
    
    int nxp1= rankdata[(n+13)%13 + 1];
    int nxp2= rankdata[(n+14)%13 + 1];
    return (nxp1 > nx + nxp2);
}

/* How good is a move? Returns 0 if the move is completely safe and should
 * be made immediately without counting this position a choice point for
 * backtracking. Positive values are lower for better moves. Negative values
 * mean the move should not be done at all.
 */
int rateMove(Stack *src, int index1, int len, Stack *dst)
{
    CourseStack *c= course + dst->tid;
    int dirscore= 30;

    /* Our least favorite thing is creating island ranks that cannot all
     * be removed, because that means the game is unwinnable */

    int rempip= src->c[index1]->n;	/* rank of card being removed */
    if (islands(rempip))
    {
	if (prune)
	{
	    return -1;  /* Prune moves that lead to nonwin states */
	}
	dirscore= 90;
    }

    /* Otherwise, we prefer NOT making a direction change */
    else if (dst->n > 2)
    {
	int toppip= dst->c[dst->n-1]->n;
	int nxtpip= dst->c[dst->n-2]->n;

	int olddir= (toppip-nxtpip + 13)%13;
	int newdir= (rempip-toppip + 13)%13;
	if (olddir != newdir) dirscore= 60;
    }

    /* Whatever else is going on, our secondary preference is to 
     * take cards from taller stacks */
    return dirscore - src->n;
}

void printState(FILE *f, Stack *stks)
{
    printFound(f, stks);
    printTableau(f, stks);
    printCourse(f, stks);
}

int victory()
{
    int off= nOff();
    if (off > maxoff) maxoff= off;
    return (off == ncard);
}

/* Return a quality rating for the current solution. Bigger is better. Usually
 * it's just cards off. This is used to decide which solution to report in
 * cases where we never win.
 */
int quality()
{
    return nOff();
}

void summary()
{
    printf("Played:    %d games\n",nplayed);
    printf("Won:       %d games (%.2f%)\n",nwon,(float)nwon*100/nplayed);
    printf("Abandoned: %d games (%.2f%)\n",nabandoned,(float)nabandoned*100/nplayed);
    printf("Cards Off: %.2f\n",(float)cardsleft/nplayed);
}

/* Return a string that uniquely describes the current state. */
char *currstate()
{
    /* card count for each tableau stack, and top card on course */
    char buf[30], *p= buf;

    /* Numbers of cards in each tableau pile */
    /* We add one to each count because we don't want any zeros */
    for (int k= 0; k < ntableau; k++)
	*(p++)= stacks[tableau[k].id].n + 1;

    /* Pip value of top card on course */
    Stack *fs= stacks + course[0].id;
    if (fs->n > 0)
	*(p++)= fs->c[fs->n-1]->n;

    *p= '\0';

    return strdup(buf);
}
